
// Copyright (c) 2024 刻BITTER
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.


#pragma once

#include <cstdint>
#include <cstdlib>


namespace oled_basic {

    /**
     * @brief 存储字体的元信息，方便使用
     *
     * 字体数组中，单个符号从左到右，按列切分成多个字节，对于宽度6，高度8 的符号，在数组中切分成6 个字节。
     * 宽度8，高度16 的符号占用两页，数组中，从上到下按页拆分成2 组，显示第一页的8 个字节，然后第二页。
     *
     * 所以，如果想访问数组中的第n 个符号，先减去offset：
     * n -= offset;
     *
     * 然后乘以每个符号占用的字节数：
     * pos = n * width * height;
     *
     * OLED 库如果想省资源，大头都在字库上，所以采用这种直观的存储方式，字库数组和元信息分开，
     * 不需要折腾编码和二进制格式，方便裁剪字库。

     * grayscale_bit 表示灰度等级，同时代表每个显示单元占用几个字节，适用于一个像素有多种灰度的高级点阵屏[doge]，
     * 单色OLED 每个像素用一个bit 表示，每次向屏幕发送一个字节就可以完整表示一列的8 个像素，
     * 但是某些有四级灰度的屏幕一个像素用2bit 表示，每次要发送两字节，才能对应一列八个像素，
     * 前一字节和后一字节的同一位组合成2bit。
     * 比如，如果前后两字节的第0 bit 组合起来是11，表示全黑，是01 和10 则表示深灰和浅灰。
     *
     * 如果灰阶屏需要2 个字节显示4 级灰度，但字库是单色的，就需要额外添加一个字节让单色字库兼容灰阶模式。
     * 一般屏幕都用1 表示黑色，0 表示白；对于灰阶屏，前后两个字节对应bit 都是1 表示黑色，10 和11 表示灰色，
     * 所以只需要把字库的数据多发一次就可以让灰阶屏显示黑色。
     * 而如果字库是4 级2bit 灰度，屏幕是16级 4bit 灰度，此时难以自动兼容，放弃处理，
     * 只要字库和屏幕的灰度等级不相等，不论是哪个更大，都只取字库里的一个字节。
     *
     * 【字体命名规则】
     *
     * 分为三段，比如ascii_6x8_no_lower_case：
     *   第一段ascii，表示字体系列名称；
     *   第二段6x8，表示单个字符占用的列和行数；6x8 字体和6x7 的实际大小是一样的，只是6x7 添加了一个空行用于分隔；
     *   第三段no_lower_case 是字体特性，或者variant；
     *
     *   行数小于等于8 占用1 页，小于等于16 占用2 页，以此类推。
     */
    struct FontMeta {
        const uint8_t* font_data;  // 指向字体数组的指针
        // size_t size;                // 单个字符的字节数 == width * height * grayscale，放在这里省得程序里每次都要做乘法
        uint8_t width;              // 字体占用的列数
        uint8_t height;             // 字体占用的页数，如果字体是6x8，则宽、高分别是6，1
        uint8_t offset;             // 字体数组中第一个元素对应的ascii 码，设为0xff，表示字体是utf16 格式
        uint8_t grayscale_bit = 1;  // 灰度位数
        // uint8_t count = 0;          // 一共包含多少个字符，0 表示未定义。这个字段通常没用，只是留作扩展，可以用来支持循环动画
    };
    
    
    struct FontMetaEx : public FontMeta {
        // TODO: UTF16 支持
        const uint16_t* map_k;   // 用作key 的utf16 编码必须按升序排列
        const uint16_t* map_v;
        uint16_t count;
        uint16_t offset_ex;
        
        uint8_t glyph_size;    // 单个字符的字节数。如果等于1，表示每个字占用的字节数都不相等；
                               // 只能查表确定偏移量。
                               // 如果等于0，表示每个字占用的字节数相等，且等于width * height，
                               // 可以直接通过字符编码和offset 顺序寻址，即默认的行为；
                               // 如果大于1，表示每个字大小相等，但需要查表；
        bool rle;
    }


    namespace font {

        extern const FontMeta ascii_6x8;
        extern const FontMeta ascii_6x8_no_lower_case;

        extern const FontMeta ascii_8x16;
        extern const FontMeta ascii_8x16_no_lower_case;
        extern const FontMeta ascii_8x16_number_only;
        extern const FontMeta ascii_8x16_upper_case_only;


        extern const FontMeta big_digit_12x24;
    }  // namespace font
}  // namespace oled_basic